// IG.cpp : Defines the exported functions for the DLL application.
//


#include "IG.h"
#include "Object.h"
#include "Camera.h"
#include "ThreadLock.h"
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgViewer/CompositeViewer>
#include <osg/StateSet>
#include "Scene.h"

// This is an example of an exported variable
IG_API int nIG=0;
ThreadLock drawLock;
Scene* sceneReal;
// This is an example of an exported function.
IG_API int fnIG(void)
{
	return 42;
}
using namespace IG;
CIG* CIG::instance = NULL;
// This is the constructor of a class that has been exported.
// see IG.h for the class definition
CIG::CIG()
{
	scene = new osg::Group;
	viewer = new osgViewer::CompositeViewer;
	//viewer->setSceneData(scene);
	sceneReal = new Scene();
	AddShader();
	return;
}
CIG* CIG::GetInstance()
{
	if (instance == NULL)
	{
		instance = new CIG();
	}
	return 	  instance;
}
void Initialize();
//return key
int CIG::AddObject(char* filename)
{
	while (drawLock.IsLocked())
		Sleep(1);
	drawLock.Lock();
	auto tmp = new Object(filename);
	int key = tmp->GetID();
	objects[key] = tmp;
	scene->addChild(tmp->Get());
	drawLock.Unlock();
	return key;
}
//return key
int CIG::AddCamera(const CameraProps& props)
{
	while (drawLock.IsLocked())
		Sleep(1);
	drawLock.Lock();
	auto tmp = new Camera(props);
	int key = tmp->GetKey();
	cameras[key] = tmp;
	tmp->Get()->setSceneData(scene);
	tmp->SetScene(sceneReal);
	viewer->addView(tmp->Get());
	drawLock.Unlock();
	return key;


}
void SetCameraSensor(int key, CAMERA_SENSOR sensor);
void SetCameraFov(int key, double fovX, double fovY, double nearPlane, double farPlane);
void SetCameraResolution(int key, double width, double height);
void CIG::Draw()//for external time control
{
	while (drawLock.IsLocked())
		Sleep(1);
	drawLock.Lock();
	viewer->frame();
	drawLock.Unlock();
}
void CIG::SetCameraLookAt(int key, double camX, double camY, double camZ, double lookX, double lookY, double lookZ, double upX, double upY, double upZ)
{
	while (drawLock.IsLocked())
		Sleep(1);
	drawLock.Lock();
	cameras[key]->SetLookAt(osg::Vec3d(camX, camY, camZ), osg::Vec3d(lookX, lookY, lookZ), osg::Vec3d(upX, upY, upZ));
	drawLock.Unlock();

}
void SetCameraPosition(int key, double x, double y, double z, double h, double p, double r);
void CIG::SetObjectPosition(int key, double x, double y, double z, double h, double p, double r)
{
	while (drawLock.IsLocked())
		Sleep(1);
	drawLock.Lock();
	objects[key]->SetObjectPosition(osg::Vec3d(x, y, z), osg::Vec3d(h, p, r));
	drawLock.Unlock();
}
//enum ThreadingModel
//{
//	SingleThreaded,
//	CullDrawThreadPerContext,
//	ThreadPerContext = CullDrawThreadPerContext,
//	DrawThreadPerContext,
//	CullThreadPerCameraDrawThreadPerContext,
//	ThreadPerCamera = CullThreadPerCameraDrawThreadPerContext,
//	AutomaticSelection
//};

void CIG::Run()
{
	while (drawLock.IsLocked())
		Sleep(1);
	drawLock.Lock();
	osgDB::writeNodeFile(*scene, "scene.osg");
	viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);
	viewer->realize();
	drawLock.Unlock();

}
//IplImage* CIG::GetImage(int cameraKey)
//{
//	return cameras[cameraKey]->GetImage();
//}
double CIG::GetVersion()
{
	return 1.0;
}
cv::Mat CIG::GetImage(int cameraKey)
{
	return  cameras[cameraKey]->GetImage();
	//image = cvCloneImage(tmp);
}
void CIG::SetSlaveCamera(int objectKey, int cameraKey)
{
	objects[objectKey]->SetSlaveCamera(cameras[cameraKey]);
}
void CIG::SetCameraOffset(int key, double x, double y, double z, double h, double p, double r)
{
	cameras[key]->SetOffset(osg::Vec3d(x, y, z), osg::Vec3d(h, p, r));
}
osg::Program * createProgram()
{

	std::stringstream vp;
	//vp << "#version 420 compatibility\n"
	//	<< "\n"
	//	<< "void main(void)\n"
	//	<< "{\n"
	//	<< "    gl_Position = ftransform();\n"
	//	<< "}\n";


	vp << "vec4 Ambient;  \n"
		<< "vec4 Diffuse;  \n"
		<< "vec4 Specular; \n"
		<<"varying vec2 texcoord;\n"
		<< "void pointLight(in int i, in vec3 normal, in vec3 eye, in vec3 ecPosition3)	\n"
		<< "{						\n"
		<< "float nDotVP;       // normal . light direction	\n"
		<< "float nDotHV;       // normal . light half vector \n"
		<< "float pf;           // power factor				\n"
		<< "float attenuation;  // computed attenuation factor \n"
		<< "float d;            // distance from surface to light source \n"
		<< "vec3  VP;           // direction from surface to light position\n"
		<< "vec3  halfVector;   // direction of maximum highlights	\n"

		<< "// Compute vector from surface to light position \n"
		<< "VP = vec3(gl_LightSource[i].position) - ecPosition3; \n"

		<< "// Compute distance between surface and light position \n"
		<< "d = length(VP);					   \n"

		<< "// Normalize the vector from surface to light position	 \n"
		<< "VP = normalize(VP);				   \n"

		<< "// Compute attenuation			 \n"
		<< "attenuation = 1.0 / (gl_LightSource[i].constantAttenuation +		\n"
		<< "	gl_LightSource[i].linearAttenuation * d +		   \n"
		<< "	gl_LightSource[i].quadraticAttenuation * d * d);   \n"

		<< "halfVector = normalize(VP + eye);		 \n"

		<< "nDotVP = max(0.0, dot(normal, VP));	   \n"
		<< "nDotHV = max(0.0, dot(normal, halfVector));		\n"

		<< "if (nDotVP == 0.0)				 \n"
		<< "{						   \n"
		<< "	pf = 0.0;			\n"
		<< "}		\n"
		<< "else  \n"
		<< "{	 \n"
		<< "	pf = pow(nDotHV, gl_FrontMaterial.shininess);	\n"

		<< "}				  \n"
		<< "Ambient += gl_LightSource[i].ambient * attenuation;				 \n"
		<< "Diffuse += gl_LightSource[i].diffuse * nDotVP * attenuation;   \n"
		<< "Specular += gl_LightSource[i].specular * pf * attenuation;	 \n"
		<< "}										   \n"

		<< "vec3 fnormal(void)	\n"
		<< "{				\n"
		<< "	//Compute the normal  \n"
		<< "	vec3 normal = gl_NormalMatrix * gl_Normal; \n"
		<< "	return normal;	 \n"
		<< "}			\n"

		<< "void ftexgen(in vec3 normal, in vec4 ecPosition) \n"
		<< "{			\n"
		<< "	gl_TexCoord[0].s = dot(gl_Vertex, gl_ObjectPlaneS[0]); \n"
		<< "	gl_TexCoord[0].t = dot(gl_Vertex, gl_ObjectPlaneT[0]); \n"
		<< "	gl_TexCoord[0].p = dot(gl_Vertex, gl_ObjectPlaneR[0]); \n"
		<< "	gl_TexCoord[0].q = dot(gl_Vertex, gl_ObjectPlaneQ[0]); \n"
		<< "}		 \n"

		<< "void flight(in vec3 normal, in vec4 ecPosition, float alphaFade)	  \n"
		<< "{														    \n"
		<< "vec4 color;												  \n"
		<< "vec3 ecPosition3;										  \n"
		<< "vec3 eye;												 \n"

		<< "ecPosition3 = (vec3(ecPosition)) / ecPosition.w;		 \n"
		<< "eye = vec3(0.0, 0.0, 1.0);							    \n"

		<< "// Clear the light intensity accumulators		    \n"
		<< "Ambient = vec4(1.0);							  \n"
		<< "Diffuse = vec4(1.0);							    \n"
		<< "Specular = vec4(1.0);							    \n"

		<< "pointLight(0, normal, eye, ecPosition3);		    \n"

		<< "color = gl_FrontLightModelProduct.sceneColor +	    \n"
		<< "	Ambient  * gl_FrontMaterial.ambient +			  \n"
		<< "	Diffuse  * gl_FrontMaterial.diffuse;			  \n"
		<< "color += Specular * gl_FrontMaterial.specular;		   \n"
		<< "color = clamp(color, 0.0, 1.0);							 \n"
		<< "gl_Color = color;									  \n"

		<< "//gl_FrontColor.a *= alphaFade;							  \n"
		<< "}														  \n"

		<< "void main(void)	  \n"
		<< "{				 \n"
		<< "	vec3  transformedNormal;   \n"
		<< "	float alphaFade = 1.0;	   \n"
		<<"    texcoord = gl_MultiTexCoord0.st;\n"
		<< "	// Eye-coordinate position of vertex, needed in various calculations	  \n"
		<< "	vec4 ecPosition = gl_ModelViewMatrix * gl_Vertex;					 \n"
		<< "	// Do fixed functionality vertex transform						\n"
		<< "	gl_Position = ftransform();									   \n"
		<< "	transformedNormal = fnormal();								   \n"
		<< "	flight(transformedNormal, ecPosition, alphaFade);	   \n"
		<<"     //   gl_FrontColor = gl_Color; \n"

		<< "	ftexgen(transformedNormal, ecPosition);					\n"
		<< "}														\n";

	osg::Shader * vpShader = new osg::Shader(osg::Shader::VERTEX, vp.str());



	std::stringstream fp;
	fp << "uniform sampler2D texUnit0;\n"
		<< "uniform int sensor;\n"
		<< "uniform float maxIntensity;\n"
		<< "uniform float minIntensity;\n"
		<< "varying vec2 texcoord;\n"
		<< "void main(void)\n"
		<< "{\n"
		<< "	vec4 color = vec4(1.0); \n"
		<< "	//color = gl_Color; \n"
		<< "	//color *= texture2D(texUnit0, gl_TexCoord[0].xy); \n"
		<< "  color *= texture2D( texUnit0, texcoord); \n"
		<< "	float gray = 0.3* color.x + 0.6 * color.y + 0.1 * color.z;\n"
		<< "	if(sensor > 0)\n"
		<< "	{\n"
		<< "		//gray\n"
		<< "		gray = gray*(maxIntensity-minIntensity)+minIntensity;\n"
		<< "		color.x = gray;\n"
		<< "		color.y = gray;\n"
		<< "		color.z = gray;\n"
		<< "	}\n"
		<< "	gl_FragColor = color; \n"
		<< "} \n";
	//fp << "#version 420 compatibility\n"
	//	<< "\n"
	//	<< "layout(binding = 0) uniform atomic_uint acRed;\n"
	//	<< "layout(binding = 0, offset = 4) uniform atomic_uint acGreen;\n"
	//	<< "layout(binding = 2) uniform atomic_uint acBlue;\n"
	//	<< "\n"
	//	<< "uniform float invNumPixel;\n"
	//	<< "\n"
	//	<< "void main(void)\n"
	//	<< "{\n"
	//	<< "    float r = float(atomicCounterIncrement(acRed)) * invNumPixel;\n"
	//	<< "    float g = float(atomicCounterIncrement(acGreen)) * invNumPixel;\n"
	//	<< "    float b = float(atomicCounterIncrement(acBlue)) * invNumPixel;\n"
	//	<< "    gl_FragColor = vec4(r, g, b, 1.0);\n"
	//	<< "}\n"
	//	<< "\n";
	osg::Shader * fpShader = new osg::Shader(osg::Shader::FRAGMENT, fp.str());

	osg::Program * program = new osg::Program;
	program->addShader(vpShader);
	program->addShader(fpShader);

	return program;
}
void CIG::AddShader()
{
	osg::StateSet * sts = scene->getOrCreateStateSet();
	sts->setAttributeAndModes(createProgram(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);
	sceneReal->sceneUniform = new osg::Uniform("sensor", (int)0);//CCD
	sts->addUniform(sceneReal->sceneUniform.get(), 0);
	sts->addUniform(new osg::Uniform("texUnit0", (int)0));
	sts->setMode(GL_LIGHTING, osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF);
	sceneReal->sceneStt = sts;
}